﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace ERF
{

    /// <summary>
    /// This class represents a game object, which is an object in the game AND a node in a scene graph. 
    /// All game objects can have children.
    /// </summary>
    public abstract class GameObject : IDisposable
    {
        protected Vector3 position = Vector3.Zero;
        protected Vector3 scale = Vector3.One;
        protected Vector3 rotation = Vector3.Zero;
        protected ERF.ERFGame game;
        protected Matrix world;
        protected bool renderWithoutDepthBuffer = false;

        private GameObject parent;
        private SynchronizedCollection<GameObject> children;
        private bool enabled = true;
        private bool visible = true;

        /// <summary>
        /// Gets or sets a value denoting if the GameObject, and all its children, will have its Update method invoked.
        /// </summary>
        public bool Enabled {
            get { return enabled; }
            set { enabled = value; }
        }

        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        public virtual Vector3 Position {
            get { return this.position; }
            set { this.position = value; }
        }

        /// <summary>
        /// Gets or sets the scale.
        /// </summary>
        public virtual Vector3 Scale {
            get { return this.scale; }
            set { this.scale = value; }
        }

        /// <summary>
        /// Gets or sets the rotation.
        /// </summary>
        public Vector3 Rotation {
            get { return this.rotation; }
            set { this.rotation = value; }
        }

        /// <summary>
        /// Gets or sets the World matrix.
        /// </summary>
        public Matrix World {
            get { return world; }
            set { world = value; }
        }

        /// <summary>
        ///  Gets or sets a value denoting if the GameObject, and all its children, will have its DrawSolid and DrawTransparent methods invoked.
        /// </summary>
        public bool Visible {
            get { return this.visible; }
            set { this.visible = value; }
        }

        /// <summary>
        /// Gets or sets a value denoting if the GameObject should be drawn without writing to the depthbuffer.
        /// </summary>
        public bool RenderWithoutDepthBuffer {
            get { return this.renderWithoutDepthBuffer; }
            set { this.renderWithoutDepthBuffer = true; }
        }

        /// <summary>
        /// Gets the children of this GameObject.
        /// </summary>
        public SynchronizedCollection<GameObject> Children {
            get { return this.children; }
        }

        /// <summary>
        ///  Gets or sets the parent of this GameObject.
        /// </summary>
        public GameObject Parent {
            get { return this.parent; }
            set { this.parent = value; }
        }

        /// <summary>
        /// Gets or sets the game that this instance belongs to.
        /// </summary>
        protected ERF.ERFGame CurrentGame {
            get { return this.game; }
            set { game = value; }
        }

        /// <summary>
        /// Gets the forward direction.
        /// </summary>
        public virtual Vector3 ForwardVector
        {
            get
            {
                return this.world.Forward;
            }
        }

        /// <summary>
        /// Creates a new GameObject without a parent.
        /// </summary>
        /// <param name="game">The game this instance belongs to.</param>
        public GameObject(ERF.ERFGame game) : this(game,null) {
        }

        /// <summary>
        /// Creats a new GameObject with a the given parent.
        /// </summary>
        /// <param name="game">The game this instance belongs to.</param>
        /// <param name="parent">The parent of this GameObject.</param>
        public GameObject(ERF.ERFGame game, GameObject parent) {
            this.children = new SynchronizedCollection<GameObject>();
            this.game = game;
            this.parent = parent;
        }

        public virtual void DrawSolid(GameTime gameTime) {
        }

        public virtual void DrawChildren(GameTime gameTime)
        {
            foreach (GameObject child in children)
            {
                if (child.Visible)
                {
                    child.DrawSolid(gameTime);
                    child.DrawTransparent(gameTime);
                }
                child.DrawChildren(gameTime);
            }
        }

        public virtual void DrawTransparent(GameTime gameTime) {
        }

        public virtual void Update(GameTime gameTime, ref Matrix world) {
            foreach (GameObject child in children) {
                child.Update(gameTime, ref world);
            }
            this.children.ApplyChanges();
        }

        protected virtual void OnSetEffectParameters(EffectBase effect) {

        }

        protected void UpdateWorld(ref Matrix world) {
            Matrix scale, rotationX, rotationY, rotationZ, translation;
            Matrix.CreateScale(ref this.scale, out scale);
            Matrix.CreateRotationX(this.rotation.X, out rotationX);
            Matrix.CreateRotationY(this.rotation.Y, out rotationY);
            Matrix.CreateRotationZ(this.rotation.Z, out rotationZ);
            Matrix.CreateTranslation(ref this.position, out translation);

            this.world = scale * rotationX * rotationY * rotationZ * translation * world;
        }

        public abstract void Dispose();
    }
}
